package com.googlecode.stil.playground;

import org.andengine.engine.camera.Camera;
import org.andengine.engine.options.EngineOptions;
import org.andengine.engine.options.ScreenOrientation;
import org.andengine.engine.options.resolutionpolicy.RatioResolutionPolicy;
import org.andengine.entity.primitive.Rectangle;
import org.andengine.entity.scene.IOnAreaTouchListener;
import org.andengine.entity.scene.IOnSceneTouchListener;
import org.andengine.entity.scene.ITouchArea;
import org.andengine.entity.scene.Scene;
import org.andengine.entity.scene.background.Background;
import org.andengine.entity.shape.IShape;
import org.andengine.extension.physics.box2d.FixedStepPhysicsWorld;
import org.andengine.extension.physics.box2d.PhysicsConnector;
import org.andengine.extension.physics.box2d.PhysicsFactory;
import org.andengine.extension.physics.box2d.PhysicsWorld;
import org.andengine.extension.physics.box2d.util.Vector2Pool;
import org.andengine.extension.physics.box2d.util.constants.PhysicsConstants;
import org.andengine.input.touch.TouchEvent;
import org.andengine.opengl.vbo.VertexBufferObjectManager;
import org.andengine.ui.activity.BaseGameActivity;

import android.hardware.SensorManager;
import android.util.Log;

import com.badlogic.gdx.math.Vector2;
import com.badlogic.gdx.physics.box2d.Body;
import com.badlogic.gdx.physics.box2d.BodyDef;
import com.badlogic.gdx.physics.box2d.BodyDef.BodyType;
import com.badlogic.gdx.physics.box2d.FixtureDef;
import com.badlogic.gdx.physics.box2d.MassData;
import com.badlogic.gdx.physics.box2d.joints.MouseJoint;
import com.badlogic.gdx.physics.box2d.joints.MouseJointDef;

public class HackySackActivity extends BaseGameActivity implements IOnSceneTouchListener, IOnAreaTouchListener {

	private static final int CAMERA_WIDTH = 720;
	private static final int CAMERA_HEIGHT = 480;

	private static final FixtureDef wallDef = PhysicsFactory.createFixtureDef(0f, 0f, 1f);
	private static final FixtureDef ballDef = PhysicsFactory.createFixtureDef(1f, 0.6f, 0.5f);

	private Camera camera;

	private Rectangle paddle;
	private Body paddleBody;
	private Rectangle ball;
	private Body balBody;
	private MouseJoint mouseJoint;
	private PhysicsWorld physics;
	private Body groundBody;

	@Override
	public EngineOptions onCreateEngineOptions() {
		camera = new Camera(0, 0, CAMERA_WIDTH, CAMERA_HEIGHT);
		return new EngineOptions(true, ScreenOrientation.LANDSCAPE_FIXED, new RatioResolutionPolicy(CAMERA_WIDTH, CAMERA_HEIGHT), camera);
	}

	@Override
	public void onCreateResources(OnCreateResourcesCallback pOnCreateResourcesCallback) {
		pOnCreateResourcesCallback.onCreateResourcesFinished();
	}

	@Override
	public void onCreateScene(OnCreateSceneCallback pOnCreateSceneCallback) {
		Scene scene = new Scene();
		final VertexBufferObjectManager vertexBufferObjectManager = this.getVertexBufferObjectManager();
		scene.setBackground(new Background(0.5f, 0.3f, 0.5f));
		physics = new FixedStepPhysicsWorld(60, new Vector2(0, SensorManager.GRAVITY_EARTH), false);

		// paddle
		MassData paddleMass = new MassData();
		paddleMass.mass = 1;
		paddle = new Rectangle((CAMERA_WIDTH - 50) / 2, (CAMERA_HEIGHT - 50) / 2, 50, 50, vertexBufferObjectManager);
		paddle.setColor(0.5f, 0.6f, 0.7f);
		scene.attachChild(paddle);
		paddleBody = PhysicsFactory.createBoxBody(physics, paddle, BodyType.KinematicBody, wallDef);
		paddleBody.setMassData(paddleMass);
		physics.registerPhysicsConnector(new PhysicsConnector(paddle, paddleBody, true, true));
		paddle.setUserData(paddleBody);

		// bal
		ball = new Rectangle((CAMERA_WIDTH - 10) / 2, 0, 10, 10, vertexBufferObjectManager);
		scene.attachChild(ball);
		balBody = PhysicsFactory.createBoxBody(physics, ball, BodyType.DynamicBody, ballDef);
		balBody.setBullet(true);
		physics.registerPhysicsConnector(new PhysicsConnector(ball, balBody, true, true));

		// temp body
		groundBody = physics.createBody(new BodyDef());

		buildWalls(scene, physics);

		scene.setOnSceneTouchListener(this);
		scene.setOnAreaTouchListener(this);
		// maak net die paddle touchable
		scene.registerTouchArea(paddle);
		// sit die physics in die scene
		scene.registerUpdateHandler(physics);
		// maak dat jou vinger nie vir die ding kan weg hardloop en die touch
		// reference verloor nie
		scene.setTouchAreaBindingOnActionDownEnabled(true);
		scene.setTouchAreaBindingOnActionMoveEnabled(true);

		pOnCreateSceneCallback.onCreateSceneFinished(scene);
	}

	@Override
	public void onPopulateScene(Scene pScene, OnPopulateSceneCallback pOnPopulateSceneCallback) {
		pOnPopulateSceneCallback.onPopulateSceneFinished();
	}
	
	private void buildWalls(Scene scene, PhysicsWorld physics) {
		final VertexBufferObjectManager vertexBufferObjectManager = this.getVertexBufferObjectManager();
		// nou bou die mure van die screen
		Rectangle roof = new Rectangle(0, 0, CAMERA_WIDTH, 5, vertexBufferObjectManager);
		Rectangle floor = new Rectangle(0, CAMERA_HEIGHT - 5, CAMERA_WIDTH, 5, vertexBufferObjectManager);
		Rectangle left = new Rectangle(0, 0, 5, CAMERA_HEIGHT, vertexBufferObjectManager);
		Rectangle right = new Rectangle(CAMERA_WIDTH - 5, 0, 5, CAMERA_HEIGHT, vertexBufferObjectManager);

		roof.setColor(1, 0, 0);
		floor.setColor(1, 1, 0);
		left.setColor(0, 0, 1);
		right.setColor(0, 1, 1);

		PhysicsFactory.createBoxBody(physics, roof, BodyType.StaticBody, wallDef);
		PhysicsFactory.createBoxBody(physics, floor, BodyType.StaticBody, wallDef);
		PhysicsFactory.createBoxBody(physics, left, BodyType.StaticBody, wallDef);
		PhysicsFactory.createBoxBody(physics, right, BodyType.StaticBody, wallDef);

		scene.attachChild(roof);
		scene.attachChild(floor);
		scene.attachChild(left);
		scene.attachChild(right);
	}

	private MouseJoint createMouseJoint(final IShape pFace, final float pTouchAreaLocalX, final float pTouchAreaLocalY) {
		final Body body = (Body) pFace.getUserData();
		Log.i("createMouseJoint", "the body is " + body);
		final MouseJointDef mouseJointDef = new MouseJointDef();

		final Vector2 localPoint = Vector2Pool.obtain((pTouchAreaLocalX - pFace.getScaleX() * 0.5f) / PhysicsConstants.PIXEL_TO_METER_RATIO_DEFAULT, (pTouchAreaLocalY - pFace.getScaleY() * 0.5f) / PhysicsConstants.PIXEL_TO_METER_RATIO_DEFAULT);
		this.groundBody.setTransform(localPoint, 0);

		mouseJointDef.bodyA = this.groundBody;
		mouseJointDef.bodyB = body;
		mouseJointDef.dampingRatio = 0.95f;
		mouseJointDef.frequencyHz = 30;
		Log.i("createMouseJoint", "the mass is " + body.getMass());
		mouseJointDef.maxForce = (200.0f * body.getMass());
		mouseJointDef.collideConnected = true;

		mouseJointDef.target.set(body.getWorldPoint(localPoint));
		Vector2Pool.recycle(localPoint);

		return (MouseJoint) this.physics.createJoint(mouseJointDef);
	}

	@Override
	public boolean onAreaTouched(TouchEvent pSceneTouchEvent, ITouchArea pTouchArea, float pTouchAreaLocalX, float pTouchAreaLocalY) {
		if (pSceneTouchEvent.isActionDown()) {
			final IShape face = (IShape) pTouchArea;
			Log.i("onAreaTouched", "the touchArea is " + pTouchArea);
			/*
			 * If we have a active MouseJoint, we are just moving it around
			 * instead of creating a second one.
			 */
			if (this.mouseJoint == null) {
				// this.mEngine.vibrate(100);
				this.mouseJoint = this.createMouseJoint(face, pTouchAreaLocalX, pTouchAreaLocalY);
			}
			return true;
		}
		return false;
	}

	@Override
	public boolean onSceneTouchEvent(Scene pScene, TouchEvent pSceneTouchEvent) {
		if (this.physics != null) {
			switch (pSceneTouchEvent.getAction()) {
				case TouchEvent.ACTION_DOWN:
					return true;
				case TouchEvent.ACTION_MOVE:
					if (this.mouseJoint != null) {
						Log.i("scene touch", "moving");
						final Vector2 vec = Vector2Pool.obtain(pSceneTouchEvent.getX() / PhysicsConstants.PIXEL_TO_METER_RATIO_DEFAULT, pSceneTouchEvent.getY() / PhysicsConstants.PIXEL_TO_METER_RATIO_DEFAULT);
						this.mouseJoint.setTarget(vec);
						Vector2Pool.recycle(vec);
					}
					return true;
				case TouchEvent.ACTION_UP:
					if (this.mouseJoint != null) {
						Log.i("scene touch", "killing joint");
						this.physics.destroyJoint(this.mouseJoint);
						this.mouseJoint = null;
					}
					return true;
			}
			return false;
		}
		return false;
	}
}